package com.ccteam.fluidmusic.fluidmusic.media.extensions

import android.annotation.SuppressLint
import android.graphics.Bitmap
import android.media.Rating
import android.net.Uri
import android.os.Bundle
import android.support.v4.media.MediaBrowserCompat
import android.support.v4.media.MediaDescriptionCompat
import android.support.v4.media.MediaMetadataCompat
import android.support.v4.media.RatingCompat
import androidx.core.net.toUri
import com.ccteam.fluidmusic.fluidmusic.common.utils.isLocalMedia
import com.ccteam.fluidmusic.fluidmusic.room.entity.MusicEntity
import com.google.android.exoplayer2.MediaItem
import com.google.android.exoplayer2.source.*
import com.google.android.exoplayer2.source.hls.HlsMediaSource
import com.google.android.exoplayer2.upstream.DataSource

/**
 *
 * @author Xiaoc
 * @since 2020/12/26
 *
 * 关于 [androidx.media2.common.MediaMetadata] 的扩展，方便调用使用
 */

/**
 * 额外字段，专辑ID信息
 */
const val METADATA_KEY_ALBUM_ID = "com.ccteam.fluidmusic.ALBUM_ID"
/**
 * 额外字段，歌手ID信息
 */
const val METADATA_KEY_ARTIST_ID = "com.ccteam.fluidmusic.ARTIST_ID"
/**
 * 额外字段，歌曲大小信息
 */
const val METADATA_KEY_SIZE = "com.ccteam.fluidmusic.SIZE"
/**
 * 额外字段，是否属于浏览项
 */
const val METADATA_KEY_BROWSER_FLAG = "com.ccteam.fluidmusic.BROWSER_FLAG"
/**
 * 额外字段，是否为离线歌曲
 * 1：离线  0：在线
 */
const val METADATA_KEY_IS_OFFLINE = "com.ccteam.fluidmusic.IS_OFFLINE"

/**
 * 当前metada处于播放列表的ID数据
 */
const val METADATA_KEY_QUEUE_ID = "com.ccteam.fluidmusic.QUEUE_ID"

const val UNKNOWN = "Unknown"

inline val MediaMetadataCompat.id: String?
    get() = getString(MediaMetadataCompat.METADATA_KEY_MEDIA_ID)!!

inline val MediaMetadataCompat.title: String?
    get() = getString(MediaMetadataCompat.METADATA_KEY_TITLE)

inline val MediaMetadataCompat.artist: String?
    get() = getString(MediaMetadataCompat.METADATA_KEY_ARTIST)

inline val MediaMetadataCompat.artistId: String?
    get() = getString(METADATA_KEY_ARTIST_ID)

inline val MediaMetadataCompat.duration
    get() = getLong(MediaMetadataCompat.METADATA_KEY_DURATION)

inline val MediaMetadataCompat.isOffline
    get() = getLong(METADATA_KEY_IS_OFFLINE)

inline val MediaMetadataCompat.author: String?
    get() = getString(MediaMetadataCompat.METADATA_KEY_AUTHOR)

inline val MediaMetadataCompat.album: String?
    get() = getString(MediaMetadataCompat.METADATA_KEY_ALBUM)

inline val MediaMetadataCompat.albumId: String?
    get() = getString(METADATA_KEY_ALBUM_ID)

inline val MediaMetadataCompat.flag: Int
    get() = getLong(METADATA_KEY_BROWSER_FLAG).toInt()

inline val MediaMetadataCompat.size: Long
    get() = getLong(METADATA_KEY_SIZE)

inline val MediaMetadataCompat.writer: String?
    get() = getString(MediaMetadataCompat.METADATA_KEY_WRITER)

inline val MediaMetadataCompat.composer: String?
    get() = getString(MediaMetadataCompat.METADATA_KEY_COMPOSER)

inline val MediaMetadataCompat.compilation: String?
    get() = getString(MediaMetadataCompat.METADATA_KEY_COMPILATION)

inline val MediaMetadataCompat.date: String?
    get() = getString(MediaMetadataCompat.METADATA_KEY_DATE)

inline val MediaMetadataCompat.year
    get() = getLong(MediaMetadataCompat.METADATA_KEY_YEAR)

inline val MediaMetadataCompat.genre: String?
    get() = getString(MediaMetadataCompat.METADATA_KEY_GENRE)

inline val MediaMetadataCompat.trackNumber
    get() = getLong(MediaMetadataCompat.METADATA_KEY_TRACK_NUMBER)

inline val MediaMetadataCompat.trackCount
    get() = getLong(MediaMetadataCompat.METADATA_KEY_NUM_TRACKS)

inline val MediaMetadataCompat.discNumber
    get() = getLong(MediaMetadataCompat.METADATA_KEY_DISC_NUMBER)

inline val MediaMetadataCompat.albumArtist: String?
    get() = getString(MediaMetadataCompat.METADATA_KEY_ALBUM_ARTIST)

inline val MediaMetadataCompat.art: Bitmap?
    get() = getBitmap(MediaMetadataCompat.METADATA_KEY_ART)

inline val MediaMetadataCompat.artUri: Uri?
    get() = getString(MediaMetadataCompat.METADATA_KEY_ART_URI)?.toUri()

inline val MediaMetadataCompat.albumArt: Bitmap?
    get() = getBitmap(MediaMetadataCompat.METADATA_KEY_ALBUM_ART)

inline val MediaMetadataCompat.albumArtUri: Uri?
    get() = getString(MediaMetadataCompat.METADATA_KEY_ALBUM_ART_URI)?.toUri()

inline val MediaMetadataCompat.rating: RatingCompat?
    get() = getRating(MediaMetadataCompat.METADATA_KEY_RATING)

inline val MediaMetadataCompat.displayTitle: String?
    get() = getString(MediaMetadataCompat.METADATA_KEY_DISPLAY_TITLE)

inline val MediaMetadataCompat.displaySubtitle: String?
    get() = getString(MediaMetadataCompat.METADATA_KEY_DISPLAY_SUBTITLE)

inline val MediaMetadataCompat.displayDescription: String?
    get() = getString(MediaMetadataCompat.METADATA_KEY_DISPLAY_DESCRIPTION)

inline val MediaMetadataCompat.displayIcon: Bitmap?
    get() = getBitmap(MediaMetadataCompat.METADATA_KEY_DISPLAY_ICON)

inline val MediaMetadataCompat.displayIconUri: Uri?
    get() = getString(MediaMetadataCompat.METADATA_KEY_DISPLAY_ICON_URI)?.toUri()

inline val MediaMetadataCompat.mediaUri: Uri?
    get() = getString(MediaMetadataCompat.METADATA_KEY_MEDIA_URI)?.toUri()

inline val MediaMetadataCompat.downloadStatus
    get() = getLong(MediaMetadataCompat.METADATA_KEY_DOWNLOAD_STATUS)

inline val MediaMetadataCompat.advertisement
    get() = getLong(MediaMetadataCompat.METADATA_KEY_ADVERTISEMENT)

//inline val MediaMetadataCompat.browsable
//    get() = getLong(MediaMetadataCompat.METADATA_KEY_BROWSABLE)
//
//inline val MediaMetadataCompat.playable
//    get() = getLong(MediaMetadataCompat.METADATA_KEY_PLAYABLE)

inline val MediaMetadataCompat.userRating: RatingCompat?
    get() = getRating(MediaMetadataCompat.METADATA_KEY_USER_RATING)

inline val MediaMetadataCompat.queueId: Long
    get() = getLong(METADATA_KEY_QUEUE_ID)

const val NO_GET = "属性不可以存在'get'方法"
const val GET_ERROR = "不可以从Builder中获取值"

inline var MediaMetadataCompat.Builder.id: String?
    @Deprecated(NO_GET, level = DeprecationLevel.ERROR)
    get() = throw IllegalAccessException(GET_ERROR)
    set(value) {
        putString(MediaMetadataCompat.METADATA_KEY_MEDIA_ID,value)
    }

inline var MediaMetadataCompat.Builder.title: String?
    @Deprecated(NO_GET, level = DeprecationLevel.ERROR)
    get() = throw IllegalAccessException(GET_ERROR)
    set(value) {
        putString(MediaMetadataCompat.METADATA_KEY_TITLE,value)
    }

inline var MediaMetadataCompat.Builder.artist: String?
    @Deprecated(NO_GET, level = DeprecationLevel.ERROR)
    get() = throw IllegalAccessException(GET_ERROR)
    set(value) {
        putString(MediaMetadataCompat.METADATA_KEY_ARTIST,value)
    }

inline var MediaMetadataCompat.Builder.artistId: String?
    @Deprecated(NO_GET, level = DeprecationLevel.ERROR)
    get() = throw IllegalAccessException(GET_ERROR)
    set(value) {
        putString(METADATA_KEY_ARTIST_ID,value)
    }

inline var MediaMetadataCompat.Builder.duration: Long
    @Deprecated(NO_GET, level = DeprecationLevel.ERROR)
    get() = throw IllegalAccessException(GET_ERROR)
    set(value) {
        putLong(MediaMetadataCompat.METADATA_KEY_DURATION,value)
    }

inline var MediaMetadataCompat.Builder.isOffline: Long
    @Deprecated(NO_GET, level = DeprecationLevel.ERROR)
    get() = throw IllegalAccessException(GET_ERROR)
    set(value) {
        putLong(METADATA_KEY_IS_OFFLINE,value)
    }

inline var MediaMetadataCompat.Builder.author: String?
    @Deprecated(NO_GET, level = DeprecationLevel.ERROR)
    get() = throw IllegalAccessException(GET_ERROR)
    set(value) {
        putString(MediaMetadataCompat.METADATA_KEY_AUTHOR,value)
    }

inline var MediaMetadataCompat.Builder.album: String?
    @Deprecated(NO_GET, level = DeprecationLevel.ERROR)
    get() = throw IllegalAccessException(GET_ERROR)
    set(value) {
        putString(MediaMetadataCompat.METADATA_KEY_ALBUM,value)
    }

inline var MediaMetadataCompat.Builder.flag: Int
    @Deprecated(NO_GET, level = DeprecationLevel.ERROR)
    get() = throw IllegalAccessException(GET_ERROR)
    set(value) {
        putLong(METADATA_KEY_BROWSER_FLAG,value.toLong())
    }

inline var MediaMetadataCompat.Builder.albumId: String?
    @Deprecated(NO_GET, level = DeprecationLevel.ERROR)
    get() = throw IllegalAccessException(GET_ERROR)
    set(value) {
        putString(METADATA_KEY_ALBUM_ID,value)
    }

inline var MediaMetadataCompat.Builder.size: Long
    @Deprecated(NO_GET, level = DeprecationLevel.ERROR)
    get() = throw IllegalAccessException(GET_ERROR)
    set(value) {
        putLong(METADATA_KEY_SIZE,value)
    }

inline var MediaMetadataCompat.Builder.writer: String?
    @Deprecated(NO_GET, level = DeprecationLevel.ERROR)
    get() = throw IllegalAccessException(GET_ERROR)
    set(value) {
        putString(MediaMetadataCompat.METADATA_KEY_WRITER,value)
    }

inline var MediaMetadataCompat.Builder.composer: String?
    @Deprecated(NO_GET, level = DeprecationLevel.ERROR)
    get() = throw IllegalAccessException(GET_ERROR)
    set(value) {
        putString(MediaMetadataCompat.METADATA_KEY_COMPOSER,value)
    }

inline var MediaMetadataCompat.Builder.compilation: String?
    @Deprecated(NO_GET, level = DeprecationLevel.ERROR)
    get() = throw IllegalAccessException(GET_ERROR)
    set(value) {
        putString(MediaMetadataCompat.METADATA_KEY_COMPILATION,value)
    }

inline var MediaMetadataCompat.Builder.date: String?
    @Deprecated(NO_GET, level = DeprecationLevel.ERROR)
    get() = throw IllegalAccessException(GET_ERROR)
    set(value) {
        putString(MediaMetadataCompat.METADATA_KEY_DATE,value)
    }

inline var MediaMetadataCompat.Builder.year: Long
    @Deprecated(NO_GET, level = DeprecationLevel.ERROR)
    get() = throw IllegalAccessException(GET_ERROR)
    set(value) {
        putLong(MediaMetadataCompat.METADATA_KEY_YEAR,value)
    }

inline var MediaMetadataCompat.Builder.genre: String?
    @Deprecated(NO_GET, level = DeprecationLevel.ERROR)
    get() = throw IllegalAccessException(GET_ERROR)
    set(value) {
        putString(MediaMetadataCompat.METADATA_KEY_GENRE,value)
    }

inline var MediaMetadataCompat.Builder.trackNumber: Long
    @Deprecated(NO_GET, level = DeprecationLevel.ERROR)
    get() = throw IllegalAccessException(GET_ERROR)
    set(value) {
        putLong(MediaMetadataCompat.METADATA_KEY_TRACK_NUMBER,value)
    }

inline var MediaMetadataCompat.Builder.trackCount: Long
    @Deprecated(NO_GET, level = DeprecationLevel.ERROR)
    get() = throw IllegalAccessException(GET_ERROR)
    set(value) {
        putLong(MediaMetadataCompat.METADATA_KEY_NUM_TRACKS,value)
    }

inline var MediaMetadataCompat.Builder.discNumber: Long
    @Deprecated(NO_GET, level = DeprecationLevel.ERROR)
    get() = throw IllegalAccessException(GET_ERROR)
    set(value) {
        putLong(MediaMetadataCompat.METADATA_KEY_DISC_NUMBER,value)
    }

inline var MediaMetadataCompat.Builder.albumArtist: String?
    @Deprecated(NO_GET, level = DeprecationLevel.ERROR)
    get() = throw IllegalAccessException(GET_ERROR)
    set(value) {
        putString(MediaMetadataCompat.METADATA_KEY_ALBUM_ARTIST,value)
    }

inline var MediaMetadataCompat.Builder.art: Bitmap?
    @Deprecated(NO_GET, level = DeprecationLevel.ERROR)
    get() = throw IllegalAccessException(GET_ERROR)
    set(value) {
        putBitmap(MediaMetadataCompat.METADATA_KEY_ART,value)
    }

inline var MediaMetadataCompat.Builder.artUri: String?
    @Deprecated(NO_GET, level = DeprecationLevel.ERROR)
    get() = throw IllegalAccessException(GET_ERROR)
    set(value) {
        putString(MediaMetadataCompat.METADATA_KEY_ART_URI,value)
    }

inline var MediaMetadataCompat.Builder.albumArt: Bitmap?
    @Deprecated(NO_GET, level = DeprecationLevel.ERROR)
    get() = throw IllegalAccessException(GET_ERROR)
    set(value) {
        putBitmap(MediaMetadataCompat.METADATA_KEY_ALBUM_ART,value)
    }

inline var MediaMetadataCompat.Builder.albumArtUri: String?
    @Deprecated(NO_GET, level = DeprecationLevel.ERROR)
    get() = throw IllegalAccessException(GET_ERROR)
    set(value) {
        putString(MediaMetadataCompat.METADATA_KEY_ALBUM_ART_URI,value)
    }

inline var MediaMetadataCompat.Builder.rating: RatingCompat?
    @Deprecated(NO_GET, level = DeprecationLevel.ERROR)
    get() = throw IllegalAccessException(GET_ERROR)
    set(value) {
        putRating(MediaMetadataCompat.METADATA_KEY_RATING,value)
    }

inline var MediaMetadataCompat.Builder.displayTitle: String?
    @Deprecated(NO_GET, level = DeprecationLevel.ERROR)
    get() = throw IllegalAccessException(GET_ERROR)
    set(value) {
        putString(MediaMetadataCompat.METADATA_KEY_DISPLAY_TITLE,value)
    }

inline var MediaMetadataCompat.Builder.displaySubtitle: String?
    @Deprecated(NO_GET, level = DeprecationLevel.ERROR)
    get() = throw IllegalAccessException(GET_ERROR)
    set(value) {
        putString(MediaMetadataCompat.METADATA_KEY_DISPLAY_SUBTITLE,value)
    }

inline var MediaMetadataCompat.Builder.displayDescription: String?
    @Deprecated(NO_GET, level = DeprecationLevel.ERROR)
    get() = throw IllegalAccessException(GET_ERROR)
    set(value) {
        putString(MediaMetadataCompat.METADATA_KEY_DISPLAY_DESCRIPTION,value)
    }

inline var MediaMetadataCompat.Builder.displayIcon: Bitmap?
    @Deprecated(NO_GET, level = DeprecationLevel.ERROR)
    get() = throw IllegalAccessException(GET_ERROR)
    set(value) {
        putBitmap(MediaMetadataCompat.METADATA_KEY_DISPLAY_ICON,value)
    }

inline var MediaMetadataCompat.Builder.displayIconUri: String?
    @Deprecated(NO_GET, level = DeprecationLevel.ERROR)
    get() = throw IllegalAccessException(GET_ERROR)
    set(value) {
        putString(MediaMetadataCompat.METADATA_KEY_DISPLAY_ICON_URI,value)
    }

inline var MediaMetadataCompat.Builder.mediaUri: String?
    @Deprecated(NO_GET, level = DeprecationLevel.ERROR)
    get() = throw IllegalAccessException(GET_ERROR)
    set(value) {
        putString(MediaMetadataCompat.METADATA_KEY_MEDIA_URI,value)
    }

inline var MediaMetadataCompat.Builder.downloadStatus: Long
    @Deprecated(NO_GET, level = DeprecationLevel.ERROR)
    get() = throw IllegalAccessException(GET_ERROR)
    set(value) {
        putLong(MediaMetadataCompat.METADATA_KEY_DOWNLOAD_STATUS,value)
    }

inline var MediaMetadataCompat.Builder.advertisement: Long
    @Deprecated(NO_GET, level = DeprecationLevel.ERROR)
    get() = throw IllegalAccessException(GET_ERROR)
    set(value) {
        putLong(MediaMetadataCompat.METADATA_KEY_ADVERTISEMENT,value)
    }

inline var MediaMetadataCompat.Builder.userRating: RatingCompat?
    @Deprecated(NO_GET, level = DeprecationLevel.ERROR)
    get() = throw IllegalAccessException(GET_ERROR)
    set(value) {
        putRating(MediaMetadataCompat.METADATA_KEY_USER_RATING,value)
    }

inline var MediaMetadataCompat.Builder.queueId: Long
    @Deprecated(NO_GET, level = DeprecationLevel.ERROR)
    get() = throw IllegalAccessException(GET_ERROR)
    set(value) {
        putLong(METADATA_KEY_QUEUE_ID,value)
    }

/**
 * 创建基于Uri的MediaSource媒体资源
 * 使用 [DefaultMediaSourceFactory] 工厂创建，该工厂自适应HLS、普通格式的URI
 * 如果是本地数据源，则
 */
fun MediaMetadataCompat.toMediaSource(cacheDataSourceFactory: DataSource.Factory,defaultFactory: DataSource.Factory): MediaSource{
    // 判断是本地Uri还是在线内容，再进行设置不同的Factory
    // 本地Uri不带Cache缓存，在线音乐带缓存
    return if(mediaUri.isLocalMedia()){
        DefaultMediaSourceFactory(defaultFactory).createMediaSource(
            MediaItem.fromUri(mediaUri!!))
    } else {
        DefaultMediaSourceFactory(cacheDataSourceFactory).createMediaSource(
            MediaItem.fromUri(mediaUri!!))
    }
}

/**
 * 将List<MediaMetadataCompat>类型的列表转换为List<MediaSource>
 * 以便播放器进行播放
 */
fun List<MediaMetadataCompat>.addToMediaSource(
    dataSourceFactory: DataSource.Factory,
    defaultFactory: DataSource.Factory
): List<MediaSource> {

    val mediaSources = mutableListOf<MediaSource>()
    forEach {
        mediaSources.add(it.toMediaSource(dataSourceFactory,defaultFactory))
    }
    return mediaSources
}

/**
 * 将MediaMetadataCompat中的信息转换到MediaDescriptionCompat中补充
 * 本地音乐因为只能传递 [MediaDescriptionCompat] 所以提供此方法
 *
 * 同时将是否离线的信息放入Description，以便通知栏进行异步加载封面
 * 这里不使用Boolean类型是因为 mediaSessionConnector 会自行封装到 metadata 中，而 metadata 不支持 Boolean
 */
fun MediaMetadataCompat.toMediaDescription(): MediaDescriptionCompat {
    val bundle = Bundle().apply {
        genre?.let {
            putString(MediaMetadataCompat.METADATA_KEY_GENRE,it)
        }
        putLong(METADATA_KEY_SIZE,size)
        putString(METADATA_KEY_ARTIST_ID,artistId)
        putString(METADATA_KEY_ALBUM_ID,albumId)
        putLong(MediaMetadataCompat.METADATA_KEY_YEAR,year)
        putLong(MediaMetadataCompat.METADATA_KEY_NUM_TRACKS,trackCount)
        putLong(MediaMetadataCompat.METADATA_KEY_TRACK_NUMBER,trackNumber)
        putLong(MediaMetadataCompat.METADATA_KEY_DURATION,duration)

    }
    return MediaDescriptionCompat.Builder().apply {
        setMediaId(id)
        setMediaUri(mediaUri)
        setSubtitle(displaySubtitle)
        setTitle(title)
        setDescription(displayDescription)
        setIconUri(albumArtUri)
        setExtras(bundle)
    }.build()

}

/**
 * 因为当前播放的内容的MediaMetada是从播放列表 [PlaylistManager] 中获取的
 * 此方法会在客户端提供 添加到播放列表 等功能时
 * 需要把传来的 [MediaDescriptionCompat] 转换成 [MediaMetadataCompat]
 * 但是由于限制，MediaDescriptionCompat内容不全，需要从extraBundle中获取内容作为补充
 */
fun MediaDescriptionCompat.toMediaMetadata(): MediaMetadataCompat{

    val metadataBuilder = MediaMetadataCompat.Builder()
        .putString(MediaMetadataCompat.METADATA_KEY_TITLE,title.toString())
        .putString(MediaMetadataCompat.METADATA_KEY_MEDIA_URI,mediaUri.toString())
        .putString(MediaMetadataCompat.METADATA_KEY_MEDIA_ID,mediaId)
        .putString(MediaMetadataCompat.METADATA_KEY_DISPLAY_SUBTITLE,subtitle.toString())
        .putString(MediaMetadataCompat.METADATA_KEY_ARTIST,subtitle.toString())
        .putString(MediaMetadataCompat.METADATA_KEY_ALBUM,description.toString())
        .putString(MediaMetadataCompat.METADATA_KEY_ALBUM_ART_URI,iconUri.toString())
        .putString(MediaMetadataCompat.METADATA_KEY_DISPLAY_ICON_URI,iconUri.toString())

    extras?.let { extra ->
        metadataBuilder.apply {
            size = extra.getLong(METADATA_KEY_SIZE,0)
            artistId = extra.getString(METADATA_KEY_ARTIST_ID)
            albumId = extra.getString(METADATA_KEY_ALBUM_ID)
            isOffline = extra.getLong(METADATA_KEY_IS_OFFLINE)
            trackCount = extra.getLong(MediaMetadataCompat.METADATA_KEY_NUM_TRACKS)
            trackNumber = extra.getLong(MediaMetadataCompat.METADATA_KEY_TRACK_NUMBER)
            duration = extra.getLong(MediaMetadataCompat.METADATA_KEY_DURATION)
        }
    }

    return metadataBuilder.build()
}

/**
 * 将 [MediaMetadataCompat] 变为 [MusicEntity] 内容
 * 需要传入 queueId 值，表示当前歌曲的队列索引
 * 在 [ExoPlayer] 中其实就是对应列表的下标
 */
fun MediaMetadataCompat.toMusicEntity(queueId: Long): MusicEntity{

    return MusicEntity(
        queueId,
        id!!,
        title ?: UNKNOWN,
    album ?: UNKNOWN,
    albumId ?: "0",
        duration,
        trackNumber,
        year,
    artist ?: UNKNOWN,
    artistId ?: "0",
        mediaUri.toString(),
        albumArtUri.toString())
}


